package com.easyadmin.service.impl;

import com.easyadmin.config.AdminConfig;
import com.easyadmin.entity.constants.Constants;
import com.easyadmin.entity.enums.*;
import com.easyadmin.entity.po.SysTask;
import com.easyadmin.entity.po.SysTaskRunLog;
import com.easyadmin.entity.query.SysTaskQuery;
import com.easyadmin.entity.query.SysTaskRunLogQuery;
import com.easyadmin.entity.vo.PaginationResult;
import com.easyadmin.entity.vo.SimplePage;
import com.easyadmin.exception.BusinessException;
import com.easyadmin.mappers.SysTaskMapper;
import com.easyadmin.mappers.SysTaskRunLogMapper;
import com.easyadmin.service.SysTaskService;
import com.easyadmin.spring.ApplicationContextProvider;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.quartz.CronExpression;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ReflectionUtils;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;


/**
 * 任务调度 业务接口实现
 */
@Service("sysTaskService")
public class SysTaskServiceImpl implements SysTaskService {

    private static final Map<Integer, ScheduledFuture> futuresMap = new ConcurrentHashMap();

    private static final Logger logger = LoggerFactory.getLogger(SysTaskServiceImpl.class);


    @Resource
    private SysTaskMapper<SysTask, SysTaskQuery> sysTaskMapper;

    @Resource
    private SysTaskRunLogMapper<SysTaskRunLog, SysTaskRunLogQuery> sysTaskRunLogMapper;

    @Resource
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;

    @Resource
    private AdminConfig adminConfig;


    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
        executor.setPoolSize(20);
        executor.setThreadNamePrefix("taskExecutor-");
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        return executor;
    }


    /**
     * 根据条件查询列表
     */
    @Override
    public List<SysTask> findListByParam(SysTaskQuery param) {
        return this.sysTaskMapper.selectList(param);
    }

    /**
     * 根据条件查询列表
     */
    @Override
    public Integer findCountByParam(SysTaskQuery param) {
        return this.sysTaskMapper.selectCount(param);
    }

    /**
     * 分页查询方法
     */
    @Override
    public PaginationResult<SysTask> findListByPage(SysTaskQuery param) {
        int count = this.sysTaskMapper.selectCount(param);
        int pageSize = param.getPageSize() == null ? PageSize.SIZE15.getSize() : param.getPageSize();
        int pageNo = 0;
        if (null != param.getPageNo()) {
            pageNo = param.getPageNo();
        }
        SimplePage page = new SimplePage(pageNo, count, pageSize);
        param.setSimplePage(page);
        List<SysTask> list = this.sysTaskMapper.selectList(param);
        PaginationResult<SysTask> result = new PaginationResult<>(count, pageSize, pageNo, page.getPageTotal(), list);
        return result;
    }

    /**
     * 新增
     */
    @Override
    public Integer add(SysTask bean) throws BusinessException {
        try {
            return this.sysTaskMapper.insert(bean);
        } catch (DuplicateKeyException e) {
            throw new BusinessException("信息已经存在");
        }
    }

    /**
     * 批量新增
     */
    @Override
    public Integer addBatch(List<SysTask> listBean) {
        if (listBean == null || listBean.isEmpty()) {
            return 0;
        }
        return this.sysTaskMapper.insertBatch(listBean);
    }

    /**
     * 批量新增或者修改
     */
    @Override
    public Integer addOrUpdateBatch(List<SysTask> listBean) {
        if (listBean == null || listBean.isEmpty()) {
            return 0;
        }
        return this.sysTaskMapper.insertOrUpdateBatch(listBean);
    }

    /**
     * 根据TaskId修改
     */
    @Override
    public Integer updateByTaskId(SysTask bean, Integer taskId) {
        return this.sysTaskMapper.updateByTaskId(bean, taskId);
    }


    /**
     * 根据TaskId删除
     */
    @Override
    public Integer deleteByTaskId(Integer taskId) {
        return this.sysTaskMapper.deleteByTaskId(taskId);
    }


    /**
     * 根据TaskId获取对象
     */
    @Override
    public SysTask getSysTaskByTaskId(Integer taskId) {
        return this.sysTaskMapper.selectByTaskId(taskId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveTask(SysTask sysTask, Boolean immediatelyRun) throws BusinessException {
        immediatelyRun = immediatelyRun == null ? false : immediatelyRun;
        if (adminConfig.getRunTask() == null || !adminConfig.getRunTask()) {
            throw new BusinessException("此机器上未开启任务调度");
        }

        if (!CronExpression.isValidExpression(sysTask.getCron())) {
            throw new BusinessException("任务执行时间不正确");
        }

        if (MethodTypeEnum.NO_PARAMS.getType().equals(sysTask.getMethodType())) {
            sysTask.setParams("");
        }

        if (sysTask.getTaskId() == null) {
            sysTask.setCreateTime(new Date());
            sysTask.setStatus(TaskStatusEnum.ENABLE.getStatus());
            sysTaskMapper.insert(sysTask);
        } else {
            sysTaskMapper.updateByTaskId(sysTask, sysTask.getTaskId());
        }

        SysTaskQuery sysTaskQuery = new SysTaskQuery();
        sysTaskQuery.setClassz(sysTask.getClassz());
        sysTaskQuery.setMethod(sysTask.getMethod());
        Integer count = sysTaskMapper.selectCount(sysTaskQuery);
        if (count > 1) {
            throw new BusinessException("任务类和任务方法已经存在");
        }

        if (TaskStatusEnum.DISABLE.getStatus().equals(sysTask.getStatus())) {
            return;
        }

        /**
         * 执行方法
         */
        runTask(sysTask, immediatelyRun);

        /**
         *  添加到任务
         */
        editTaskQueue(sysTask, TaskStatusEnum.ENABLE);
    }


    @Override
    public void immediatelyRunTask(Integer taskId) throws BusinessException {
        if (adminConfig.getRunTask() == null || !adminConfig.getRunTask()) {
            throw new BusinessException("此机器上未开启任务调度");
        }
        SysTask sysTask = this.sysTaskMapper.selectByTaskId(taskId);
        if (sysTask == null) {
            throw new BusinessException("任务不存在");
        }

        if (TaskStatusEnum.DISABLE.getStatus().equals(sysTask.getStatus())) {
            throw new BusinessException("任务暂停，无法执行");
        }

        runTask(sysTask, true);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void suspendAndStartTask(Integer taskId, Integer opStatus) throws BusinessException {
        if (adminConfig.getRunTask() == null || !adminConfig.getRunTask()) {
            throw new BusinessException("此机器上未开启任务调度");
        }
        SysTask sysTask = this.sysTaskMapper.selectByTaskId(taskId);
        if (sysTask == null) {
            throw new BusinessException("任务不存在");
        }
        sysTask.setStatus(opStatus);
        this.sysTaskMapper.updateByTaskId(sysTask, taskId);
        editTaskQueue(sysTask, TaskStatusEnum.getByStatus(opStatus));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteTask(Integer taskId) throws BusinessException {
        if (adminConfig.getRunTask() == null || !adminConfig.getRunTask()) {
            throw new BusinessException("此机器上未开启任务调度");
        }
        SysTask sysTask = this.sysTaskMapper.selectByTaskId(taskId);
        if (sysTask == null) {
            throw new BusinessException("任务不存在");
        }
        editTaskQueue(sysTask, TaskStatusEnum.DISABLE);
        this.sysTaskMapper.deleteByTaskId(taskId);
    }

    private void runTask(SysTask sysTask, Boolean immediatelyRun) throws BusinessException {
        SysTaskRunLog runLog = new SysTaskRunLog();
        runLog.setTaskId(sysTask.getTaskId());
        TaskRunResultEnum runResultEnum = TaskRunResultEnum.SUCCESS;
        String errorInfo = null;
        try {
            runLog.setStartTime(new Date());
            Object bean = null;
            if (sysTask.getClassz().contains(".")) {
                bean = ApplicationContextProvider.getBean(Class.forName(sysTask.getClassz()));
            } else {
                bean = ApplicationContextProvider.getBean(sysTask.getClassz());
                if (null == bean) {
                    throw new BusinessException("任务类springBean不存在");
                }
            }

            MethodTypeEnum methodTypeEnum = MethodTypeEnum.getByType(sysTask.getMethodType());
            if (null == methodTypeEnum) {
                throw new BusinessException(ResponseCodeEnum.CODE_600);
            }
            Method method = null;
            if (MethodTypeEnum.NO_PARAMS == methodTypeEnum) {
                method = ReflectionUtils.findMethod(bean.getClass(), sysTask.getMethod());
            } else if (MethodTypeEnum.PARAMS == methodTypeEnum) {
                method = ReflectionUtils.findMethod(bean.getClass(), sysTask.getMethod(), String.class);
            }
            if (method == null) {
                throw new BusinessException("任务方法不存在");
            }
            Integer parameterCount = method.getParameterCount();
            if (parameterCount > 1) {
                throw new BusinessException("【" + sysTask.getMethod() + "】方法最多只能有一个参数");
            }
            if (immediatelyRun && parameterCount == 0) {
                ReflectionUtils.invokeMethod(method, bean);
            } else if (immediatelyRun && parameterCount == 1) {
                ReflectionUtils.invokeMethod(method, bean, sysTask.getParams());
            }
        } catch (ClassNotFoundException e) {
            runResultEnum = TaskRunResultEnum.ERROR;
            logger.error("任务类：【{}】不存在", sysTask.getClassz(), e);
            errorInfo = ExceptionUtils.getStackTrace(e);
            throw new BusinessException("任务类不存在");
        } catch (BusinessException e) {
            runResultEnum = TaskRunResultEnum.ERROR;
            logger.error("任务类：【{}】执行失败", sysTask.getClassz(), e);
            errorInfo = ExceptionUtils.getStackTrace(e);
            throw e;
        } catch (Exception e) {
            runResultEnum = TaskRunResultEnum.ERROR;
            logger.error("任务类：【{}】执行失败", sysTask.getClassz(), e);
            errorInfo = ExceptionUtils.getStackTrace(e);
            throw new BusinessException("执行任务失败");
        } finally {
            runLog.setEndTime(new Date());
            runLog.setResult(runResultEnum.getResult());
            runLog.setErrorInfo(errorInfo);
            if (immediatelyRun && Constants.ONE.equals(sysTask.getRecordRunLogType())) {
                this.sysTaskRunLogMapper.insert(runLog);
            }
        }
    }


    /**
     * 编辑任务队列
     *
     * @param sysTask
     */
    @Override
    public void editTaskQueue(SysTask sysTask, TaskStatusEnum taskStatus) {
        ScheduledFuture scheduledFutur = futuresMap.get(sysTask.getTaskId());
        if (scheduledFutur != null) {
            if (scheduledFutur != null) {
                scheduledFutur.cancel(Boolean.TRUE);
            }
        }
        if (taskStatus == TaskStatusEnum.DISABLE) {
            return;
        }
        scheduledFutur = threadPoolTaskScheduler.schedule(new Runnable() {
            @Override
            public void run() {
                try {
                    runTask(sysTask, true);
                } catch (BusinessException e) {
                    logger.error("任务,classz:{},method:{},params:{},执行失败", sysTask.getClassz(), sysTask.getMethod(), sysTask.getParams(), e);
                }
            }
        }, new CronTrigger(sysTask.getCron()));
        futuresMap.put(sysTask.getTaskId(), scheduledFutur);
    }
}